package net.cyue.easyconfiguration.node.util;

import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.easyconfiguration.node.converter.DefaultConverter;
import net.cyue.easyconfiguration.node.data.NodeMetaData;
import net.cyue.easyconfiguration.node.definition.AbstractNodeDefinition;
import net.cyue.easyconfiguration.node.definition.ArrayNodeDefinition;
import net.cyue.easyconfiguration.node.definition.TableNodeDefinition;
import net.cyue.util.ObjectConverter;
import net.cyue.util.ObjectUtil;
import net.cyue.util.ReflectUtil;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class NodeValueConverter {

    private static final Logger LOGGER = Logger.getLogger(NodeValueConverter.class.getName());

    /**
     * 转换节点值到目标类型
     */
    public static Object convertNodeValue(AbstractNodeDefinition definition, Object value) {
        if (value == null) {
            return null;
        }

        if (definition.getType() == NodeType.ARRAY) {
            return convertArrayValue(
                (ArrayNodeDefinition) definition,
                value
            );
        } else if (
            definition.getType() == NodeType.TABLE &&
            value instanceof Map
        ) {
            return convertTableValue(
                (TableNodeDefinition) definition,
                (Map<String, Object>) value
            );
        }
        // 默认转换
        if (
            definition.getTypeConverter() != null &&
            !(definition.getTypeConverter() instanceof DefaultConverter)
        ) {
            return definition.convert(value);
        } else if (definition.isForceConvert()) {
            return forceConvertValue(definition.getJavaType(), value);
        }
        return value;
    }

    /**
     * 转换数组值
     */
    public static Object convertArrayValue(
        ArrayNodeDefinition arrayDefinition,
        Object value
    ) {
        AbstractNodeDefinition elementDefinition = arrayDefinition.getElementDefinition();
        Object[] tempArray;
        if (value.getClass().isArray()) {
            Object[] array = (Object[]) value;
            tempArray = new Object[array.length];
            for (int i = 0; i < array.length; i++) {
                tempArray[i] = convertNodeValue(elementDefinition, array[i]);
            }
        } else {
            tempArray = new Object[]{ convertNodeValue(elementDefinition, value) };
        }
        // 转换
        if (
            arrayDefinition.getTypeConverter() != null &&
            !(arrayDefinition.getTypeConverter() instanceof DefaultConverter)
        ) {
            // 被转换为数组或其他类型数据（如Set，List）
            Object converted = arrayDefinition.convert(tempArray);
            if (converted != null) {
                return converted;
            }
        }
        // 强制转换
        else if (arrayDefinition.isForceConvert()) {
            Object converted = forceConvertValue(arrayDefinition.getJavaType(), tempArray);
            // 转换数组类型
            if (converted.getClass().isArray()) {
                Class<?> componentType;
                Object[] convertedArray = (Object[]) converted;
                if (convertedArray.length == 0 || convertedArray[0] == null) {
                    componentType = arrayDefinition.getElementJavaType();
                } else {
                    componentType = convertedArray[0].getClass();
                }
                return ArrayUtil.cast(
                    convertedArray,
                    componentType
                );
            }
            return converted;
        }

        // 转换数组类型（基于数据类型）
        if (tempArray.length == 0) {
            return tempArray;
        }
        return ArrayUtil.cast(tempArray, tempArray[0].getClass());
    }


    /**
     * 转换表值
     *
     * @param tableDefinition 表数据定义
     * @param table 预处理后的表
     */
    public static Object convertTableValue(
        TableNodeDefinition tableDefinition,
        Map<String, Object> table
    ) {
        Map<String, Object> convertedTable = new HashMap<>();
        for (AbstractNodeDefinition fieldDefinition : tableDefinition.getFieldDefinitions()) {
            Object value = convertNodeValue(
                fieldDefinition,
                table.get(fieldDefinition.getName())
            );
            if (value != null) {
                convertedTable.put(
                    fieldDefinition.getName(),
                    value
                );
            }
        }
        if (
            tableDefinition.getTypeConverter() != null &&
            !(tableDefinition.getTypeConverter() instanceof DefaultConverter)
        ) {
            return tableDefinition.convert(convertedTable);
        }
        if (tableDefinition.isForceConvert()) {
            return forceConvertValue(tableDefinition.getJavaType(), convertedTable);
        }
        return convertedTable;
    }


    /**
     * 强制转换值
     * @param targetType 要转换的类型
     * @param value 已预处理的节点值
     * @return 强制转换后的值
     */
    public static Object forceConvertValue(Class<?> targetType, Object value) {
        // 强制转换
        if (targetType == null || value == null || targetType == Object.class) {
            // value 不一定为 null
            return value;
        }

        // 值类型相同
        if (targetType.isInstance(value)) {
            return value;
        }

        // 根据 JavaType 转换
        if (targetType.isPrimitive()) {
            return forceConvertValue(ObjectUtil.getPrimitiveWrapperType(targetType), value);
        } else if (String.class.isAssignableFrom(targetType)) {
            return value.toString();
        } else if (Number.class.isAssignableFrom(targetType)) {
            return convertValue2Number(value);
        } else if (targetType.equals(Boolean.class)) {
            return convertValue2Boolean(value);
        } else if (targetType.isEnum()) {
            return convertValue2Enum(
                (Class<? extends Enum>) targetType,
                value
            );
        } else if (targetType.isInterface()) {
            return convertValue2Instance(targetType, value);
        } else if (targetType.isAnnotation()) {
            LOGGER.warning("无法将节点值 " + value + " 转为 " + targetType.getName() + " 注解对象");
            return value;
        } else if (targetType.isArray()) {
            return convertValue2Array(value);
        } else if (List.class.isAssignableFrom(targetType)) {
            return convertValue2List(value);
        } else if (Set.class.isAssignableFrom(targetType)) {
            return convertValue2Set(value);
        } else if (Map.class.isAssignableFrom(targetType)) {
            return convertValue2Map(value);
        } else if (targetType == Class.class) {
            try {
                return Thread.currentThread().getContextClassLoader().loadClass(value.toString());
            } catch (ClassNotFoundException e) {
                LOGGER.warning(e.toString());
                return null;
            }
        }

        // 转为 bean
        try {
            return convertValue2Bean(targetType, value);
        } catch (NoSuchMethodException | InvocationTargetException e) {
            LOGGER.warning(e.toString());
            LOGGER.warning("无法将节点值 " + value + " 转为 " + targetType.getName() + " 对象");
            return value;
        }
    }

    public static Number convertValue2Number(Object value) {
        if (value instanceof Number) {
            return (Number) value;
        }
        return ObjectConverter.convert(value, Number.class);
    }
    /**
     * 转换为布尔类型
     */
    public static Boolean convertValue2Boolean(Object value) {
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        }
        String strValue = value.toString().trim().toLowerCase();
        return "true".equals(strValue) || "1".equals(strValue) || "yes".equals(strValue);
    }
    public static <T extends Enum<T>> T convertValue2Enum(
        Class<T> clz,
        Object value
    ) {
        return Enum.valueOf(
            clz,
            value.toString().toUpperCase()
        );
    }
    public static <T> T convertValue2Instance(Class<T> clz, Object value) {
        if (value == null) {
            return null;
        }
        if (clz.isInstance(value)) {
            return clz.cast(value);
        }
        try {
            Class<?> instanceClz = Thread.currentThread()
                    .getContextClassLoader()
                    .loadClass(value.toString());
            return (T) ReflectUtil.createInstance(instanceClz);
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            LOGGER.warning(e.toString());
            return null;
        }
    }

    public static Object[] convertValue2Array(Object value) {
        return convertValue2Array(value, Object.class);
    }

    /**
     *
     * @param value
     * @param targetElementClz ComponentType 元素类型
     * @return
     */
    public static Object[] convertValue2Array(Object value, Class<?> targetElementClz) {
        Class<?> componentType;
        if (targetElementClz == null || targetElementClz == Object.class) {
            componentType = value.getClass().isArray() ? value.getClass().getComponentType() : value.getClass();
        } else {
            componentType = targetElementClz;
        }

        AtomicInteger indexRef = new AtomicInteger(0);
        if (value instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) value;
            Object[] array = (Object[]) Array.newInstance(componentType, map.size());

            map.values()
                .stream()
                .map(v -> forceConvertValue(componentType, v))
                .forEach(v -> array[indexRef.getAndIncrement()] = v);
            return array;
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            Object[] array = (Object[]) Array.newInstance(componentType, list.size());

            list.stream()
                .map(v -> forceConvertValue(componentType, v))
                .forEach(v -> array[indexRef.getAndIncrement()] = v);
            return array;
        } else if (value instanceof Set) {
            Set<?> set = (Set<?>) value;
            Object[] array = (Object[]) Array.newInstance(componentType, set.size());

            set.stream()
                .map(v -> forceConvertValue(componentType, v))
                .forEach(v -> array[indexRef.getAndIncrement()] = v);
            return array;
        } else if (value.getClass().isArray()) {
            return ArrayUtil.cast(
                (Object[]) value,
                componentType
            );
        }
        return ArrayUtil.cast(
            new Object[]{ value },
            componentType
        );
    }

    public static List<?> convertValue2List(Object value) {
        return convertValue2List(value, Object.class);
    }
    public static List<?> convertValue2List(Object value, Class<?> targetElementClz) {
        if (value instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) value;
            return map.values()
                .stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toList());
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            return list.stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toList());
        } else if (value instanceof Set) {
            Set<?> set = (Set<?>) value;
            return set.stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toList());
        } else if (value.getClass().isArray()) {
            return new ArrayList<>(Arrays.asList(convertValue2Array(value, targetElementClz)));
        }
        return Collections.singletonList(forceConvertValue(targetElementClz, value));
    }

    public static Set<?> convertValue2Set(Object value) {
        return convertValue2Set(value, Object.class);
    }
    public static Set<?> convertValue2Set(Object value, Class<?> targetElementClz) {
        if (value instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) value;
            return map.values()
                .stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toSet());
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            return list.stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toSet());
        } else if (value instanceof Set) {
            Set<?> set = (Set<?>) value;
            return set.stream()
                .map(v -> forceConvertValue(targetElementClz, v))
                .collect(Collectors.toSet());
        } else if (value.getClass().isArray()) {
            return new HashSet<>(Arrays.asList(convertValue2Array(value, targetElementClz)));
        }
        return new HashSet<>(Collections.singletonList(forceConvertValue(targetElementClz, value)));
    }

    public static Map<String, Object> convertValue2Map(Object value) {
        return convertValue2Map(value, Object.class);
    }
    public static Map<String, Object> convertValue2Map(Object value, Class<?> targetElementClz) {
        Map<String, Object> map = new HashMap<>();
        if (value instanceof Map) {
            Map<Object, Object> valueMap = (Map<Object, Object>) value;
            for (Map.Entry<?, ?> entry : valueMap.entrySet()) {
                map.put(
                    entry.getKey().toString(),
                    forceConvertValue(targetElementClz, entry.getValue())
                );
            }
        } else if (value.getClass().isArray()) {
            Object[] array = (Object[]) value;
            for (int i = 0; i < array.length; i++) {
                map.put(
                    String.valueOf(i),
                    forceConvertValue(targetElementClz, array[i])
                );
            }
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            for (int i = 0; i < list.size(); i++) {
                map.put(
                    String.valueOf(i),
                    forceConvertValue(targetElementClz, list.get(i))
                );
            }
        } else if (value instanceof Set) {
            Set<?> set = (Set<?>) value;
            Object[] values = set.toArray();
            for (int i = 0; i < set.size(); i++) {
                map.put(
                    String.valueOf(i),
                    forceConvertValue(targetElementClz, values[i])
                );
            }
        }
        // 基本类型无法转为 map
        else if (ObjectUtil.isPrimitive(value) || ObjectUtil.isString(value)) {
            return map;
        }
        // bean
        else {
            // bean 不转换
            for (Field f : ReflectUtil.getAllFields(value.getClass(), false)) {
                map.put(
                    f.getName(),
                    ReflectUtil.getFieldValue(value, f)
                );
            }
        }

        return map;
    }
    /**
     *
     * @param clazz 目标类
     * @param value 已转换的的节点值
     * @return 目标类实例
     * @param <T> 目标类型
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    public static <T> T convertValue2Bean(
        Class<T> clazz,
        Object value
    ) throws NoSuchMethodException, InvocationTargetException {
        // 基本数据类型 或 String，默认使用 ObjectConverter 转换，可自行注册转换器
        if (ObjectUtil.isPrimitive(value) || ObjectUtil.isString(value)) {
            try {
                return ObjectConverter.convert(value, clazz);
            } catch (ObjectConverter.ConversionException e) {
                return null;
            }
        }

        if (value instanceof Map) {
            return convertTableValue2Bean(clazz, (Map<String, Object>) value);
        }
        Map<String, Object> map = convertValue2Map(value);
        return convertTableValue2Bean(clazz, map);
    }
    /**
     * 将 table 转为 bean，不考虑 definition
     * @param clazz 目标类
     * @param table 已转换的的节点值
     * @return 转换后的 bean
     * @param <T> 目标类型
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    public static <T> T convertTableValue2Bean(
        Class<T> clazz,
        Map<String, Object> table
    ) throws NoSuchMethodException, InvocationTargetException
    {
        if (clazz == null || table == null) {
            LOGGER.warning("clazz or table is null");
            return null;
        }
        T bean = ReflectUtil.createInstance(clazz);
        for(Field f : ReflectUtil.getAllFields(clazz)) {
            NodeMetaData metaData = NodeMetaData.fromField(f);
            Object nodeValue = table.get(metaData.getName());
            // 处理数组类型转换
            if (
                nodeValue != null &&
                nodeValue.getClass().isArray() &&
                f.getType().isArray() &&
                !f.getType().equals(nodeValue.getClass())
            ) {
                // 如果类型不匹配，尝试转换数组元素类型
                nodeValue = convertValue2Array(nodeValue);
            }
            // 保留默认值
            if (nodeValue != null) {
                ReflectUtil.setFieldValue(bean, f, nodeValue);
            }
        }
        return bean;
    }
}
